package main;

import labyrinth.Labyrinth;
import labyrinth.Map;
import labyrinth.Position;
import agent.IAgent;
import agent.moves.IAgentMove;

/**
 * The main loop for the pathfinding
 * 
 * @author Johannes Kulick
 * @version 0.1
 * @licence GNU Lesser General Public License
 */
public class LabyrinthLoop extends Thread {

	private IAgent mAgent;
	private LabyrinthControl mControl;
	private Labyrinth mLabyrinth;

	/**
	 * Standard c'tor
	 * 
	 * @param agent
	 *            The moving agent
	 * @param control
	 *            The control object for this Labyrinth
	 * @param labyrinth
	 *            The labyrinth
	 */
	public LabyrinthLoop(IAgent agent, LabyrinthControl control,
			Labyrinth labyrinth) {
		mAgent = agent;
		mControl = control;
		mLabyrinth = labyrinth;
	}

	@Override
	public void run() {

		Position agentPosition = mLabyrinth.getStartPoint();
		Position movePosition = new Position();

		Map map = new Map(mLabyrinth);
		IAgentMove oldMove = null;
		IAgentMove move = null;

		while (!isInterrupted() && !hasArrived(agentPosition)) {

			map = getMap(agentPosition, map);
			move = mAgent.getNextMove(map, oldMove);
			movePosition = agentPosition.move(move);
			if (movePosition.x <= map.getSize().x
					&& movePosition.x >= 0
					&& movePosition.y <= map.getSize().y
					&& movePosition.y >= 0
					&& mLabyrinth.getMatrix()[movePosition.x][movePosition.y] != Labyrinth.WALL) {
				try {
					mControl.move(move);
				} catch (InterruptedException e) {
					interrupt();
					break;
				}
				oldMove = move;
				agentPosition = movePosition;
			} else {
				oldMove = null;
			}
		}
		if (hasArrived(agentPosition)) {
			mControl.arrived();
		}
	}

	/**
	 * Creates the map we hand over to the agent
	 * 
	 * @param agentPosition
	 *            The current position of the agent
	 * @param oldMap
	 *            The old agent map
	 * @return The new map
	 */
	private Map getMap(Position agentPosition, Map oldMap) {
		Map result = new Map(mLabyrinth.getMatrix());
		if(mControl.getDifficulty() == LabyrinthControl.NORMAL) {
			for (int x = 0; x < mLabyrinth.getMatrix().length; x++) {
				for (int y = 0; y < mLabyrinth.getMatrix()[x].length; y++) {
					result.set(new Position(y,x),mLabyrinth.getMatrix()[x][y]);
				}
			}
			return result;
		}
		else {
			for (int x = 0; x < mLabyrinth.getMatrix().length; x++) {
				for (int y = 0; y < mLabyrinth.getMatrix()[x].length; y++) {
					if(mLabyrinth.isLineOfSight(agentPosition, new Position(x,y))) {
						for(int i = Math.max(x-1,0); i <= Math.min(x,mLabyrinth.getMatrix().length-1); ++i) {
							for(int j = Math.max(y-1,0); j <=  Math.min(y,mLabyrinth.getMatrix()[0].length-1); ++j) {
								oldMap.set(new Position(j, i), mLabyrinth.getMatrix()[i][j]);
							}
						}
					}
				}
			}
			return oldMap;
		}
	}

	
	/**
	 * Has the agent arrived the goal?
	 * 
	 * @param agentPosition
	 *            The current agent position
	 * @return @TRUE if the agent is at the goal, @FALSE else
	 */
	private boolean hasArrived(Position agentPosition) {
		return mLabyrinth.getMatrix()[agentPosition.x][agentPosition.y] == Labyrinth.GOAL;
	}

}
